/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jmodcog2.featuremap.som;

/**
 *
 * @author Mitch
 */
public class SOMLearner extends SOMRunner {

    final float ALPHA, SLOPE, R;           //quadratic learning constancts
    final int T;
    final float[] t = new float[]{0};
    final float[] mu;
    final float[] r;

    protected SOMLearner(float alpha, float slope, float R, int T, int som_width, int som_height, int input_qty, int input_len, float[] som_weights) {
        super(som_width, som_height, input_qty, input_len, som_weights);
        this.ALPHA = alpha;
        this.SLOPE = slope;
        this.R = R;
        this.T = T;
        this.mu = new float[input_qty];
        this.r = new float[input_qty];
        put(t);
        put(mu);
        put(r);
    }
    static final int MD_SET_LEARN_RATE = 2;
    static final int MD_LEARN = 3;

    @Override
    protected void _run(int mode) {
        int gid = getGlobalId(0);
        if (mode == MD_SET_LEARN_RATE) {
            this.set_learn(gid);
        }
        if (mode == MD_LEARN) {
            int pid = getPassId();
            this.calc_err(pid, gid);
            this.calc_win(pid);
            this.learn(pid, gid);
        }
    }

    protected void adjust_weight(int input_n, int weight_ind, int input_ind, float dist_decay) {
        som_weights[weight_ind] += dist_decay * mu[input_n] * (input[input_ind] - som_weights[weight_ind]);
    }

    protected float dist_decay(int input_n, int som_n) {
        float di = 0;
        float dj = 0;
        float dSq = 0;
        int wi = input_n * 2;
        di = (som_i(som_n) + 0.5f) - (winners[wi]);
        dj = (som_j(som_n) + 0.5f) - (winners[wi + 1]);
        dSq = di * di + dj * dj;
        float rad = this.r[input_n];
        return exp(-dSq / (2 * rad * rad));
    }

    protected void learn(int input_n, int som_n) {
        float d_dec = 0;
        int w_ind = som_w0_index(som_n);
        int i_ind = input_w0_index(input_n);

        d_dec = dist_decay(input_n, som_n);

        for (int w = 0; w < input_len; w++) {
            adjust_weight(input_n, w_ind + w, i_ind + w, d_dec);
        }
    }

    protected void set_learn(int input_n) {
        float tT = (t[0] + input_n) / ((float) T);
        mu[input_n] = ALPHA * SLOPE * tT * exp(-SLOPE * tT);
        r[input_n] = R * exp(-tT);
    }

    @Override
    public boolean process(float[] input) {
        if (input.length == this.input.length) {
            setMode(MD_SET_LEARN_RATE);
            execute(input_qty);
            setMode(MD_LEARN);
            execute(som_qty, input_qty);

            get(t);
            t[0] = t[0] + input_qty;
            put(t);
            return true;
        }
        return false;
    }
}
